home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************
- * CagdALen.c - arc length functions and unit length normalization. *
- *******************************************************************************
- * Written by Gershon Elber, Apr. 93. *
- ******************************************************************************/
-
- #include "cagd_loc.h"
-
- #define MAX_ALEN_IMPROVE_ITERS 5
- #define ARCLEN_CONST_SET_EPSILON 0.001
-
- /******************************************************************************
- * Subdivides the given curves to curves, each with less of control polygon *
- * less than or equal to MaxLen. Returned is a list of curves. *
- ******************************************************************************/
- CagdCrvStruct *CagdLimitCrvArcLen(CagdCrvStruct *Crv, CagdRType MaxLen)
- {
- if (CagdCrvArcLenPoly(Crv) > MaxLen) {
- CagdRType TMin, TMax;
- CagdCrvStruct *Crv1, *Crv2, *Crv1MaxLen, *Crv2MaxLen;
-
- CagdCrvDomain(Crv, &TMin, &TMax);
-
- Crv1 = CagdCrvSubdivAtParam(Crv, (TMin + TMax) / 2.0);
- Crv2 = Crv1 -> Pnext;
-
- Crv1MaxLen = CagdLimitCrvArcLen(Crv1, MaxLen);
- Crv2MaxLen = CagdLimitCrvArcLen(Crv2, MaxLen);
-
- CagdCrvFree(Crv1);
- CagdCrvFree(Crv2);
-
- for (Crv1 = Crv1MaxLen; Crv1 -> Pnext != NULL; Crv1 = Crv1 -> Pnext);
- Crv1 -> Pnext = Crv2MaxLen;
- return Crv1MaxLen;
- }
- else
- return CagdCrvCopy(Crv);
- }
-
- /******************************************************************************
- * Computes a bound on the arc length of a curve by computing the length of *
- * its control polygon. *
- ******************************************************************************/
- CagdRType CagdCrvArcLenPoly(CagdCrvStruct *Crv)
- {
- int i;
- CagdCrvStruct
- *CrvE3 = CagdCoerceCrvTo(Crv, CAGD_PT_E3_TYPE);
- CagdRType
- Len = 0.0,
- **Points = CrvE3 -> Points;
-
- for (i = 1; i < CrvE3 -> Length; i++)
- Len += sqrt(SQR(Points[1][i] - Points[1][i - 1]) +
- SQR(Points[2][i] - Points[2][i - 1]) +
- SQR(Points[3][i] - Points[3][i - 1]));
-
- CagdCrvFree(CrvE3);
-
- return Len;
- }
-
- /******************************************************************************
- * Normalizes the given curve to be a unit length curve, by computing a scalar *
- * curve to multiply with this curve. Returns the multiplied curve if Mult, or *
- * just the scalar curve, otherwise. *
- ******************************************************************************/
- CagdCrvStruct *CagdCrvUnitLenScalar(CagdCrvStruct *OrigCrv, CagdBType Mult,
- CagdRType Epsilon)
- {
- int i, j;
- CagdCrvStruct
- *ScalarCrv = NULL,
- *Crv = CAGD_IS_BEZIER_CRV(OrigCrv) ? CnvrtBezier2BsplineCrv(OrigCrv)
- : CagdCrvCopy(OrigCrv);
- CagdBType
- IsRational = CAGD_IS_RATIONAL_CRV(Crv);
-
- for (i = 0; i < MAX_ALEN_IMPROVE_ITERS; i++) {
- CagdCrvStruct *UnitCrvAprx, *ScalarCrvSqr,
- *DotProdCrv = CagdCrvDotProd(Crv, Crv);
- CagdRType Min, Max, *SCPoints,
- *DPPoints = DotProdCrv -> Points[1];
-
- if (ScalarCrv)
- CagdCrvFree(ScalarCrv);
- ScalarCrv = CagdCrvCopy(DotProdCrv);
- SCPoints = ScalarCrv -> Points[1];
-
- /* Compute an approximation to the inverse function. */
- for (j = 0; j < ScalarCrv -> Length; j++, DPPoints++) {
- *SCPoints++ = *DPPoints > 0.0 ? 1.0 / sqrt(*DPPoints) : 1.0;
- }
- ScalarCrvSqr = CagdCrvMult(ScalarCrv, ScalarCrv);
-
- /* Multiply the two to test the error. */
- UnitCrvAprx = CagdCrvMult(ScalarCrvSqr, DotProdCrv);
- CagdCrvFree(ScalarCrvSqr);
-
- CagdCrvMinMax(UnitCrvAprx, 1, &Min, &Max);
-
- if (1.0 - Min < Epsilon && Max - 1.0 < Epsilon) {
- CagdCrvFree(UnitCrvAprx);
- CagdCrvFree(DotProdCrv);
- break;
- }
- else {
- /* Refine in regions where the error is too large. */
- int k,
- Length = UnitCrvAprx -> Length,
- Order = UnitCrvAprx -> Order,
- KVLen = Length + Order;
- CagdRType
- *KV = UnitCrvAprx -> KnotVector,
- *RefKV = IritMalloc(sizeof(CagdRType) * 2 * Length),
- *Nodes = BspKnotNodes(KV, KVLen, Order),
- **Points = UnitCrvAprx -> Points;
-
- for (j = k = 0; j < Length; j++) {
- CagdRType
- V = 1.0 - (IsRational ? Points[1][j] / Points[0][j]
- : Points[1][j]);
-
- if (ABS(V) > Epsilon) {
- int Index = BspKnotLastIndexLE(KV, KVLen, Nodes[j]);
-
- if (APX_EQ(KV[Index], Nodes[j])) {
- if (j > 0)
- RefKV[k++] = (Nodes[j] + Nodes[j - 1]) / 2.0;
- if (j < Length - 1)
- RefKV[k++] = (Nodes[j] + Nodes[j + 1]) / 2.0;
- }
- else
- RefKV[k++] = Nodes[j];
- }
- }
- CagdCrvFree(UnitCrvAprx);
- CagdCrvFree(DotProdCrv);
-
- IritFree((VoidPtr) Nodes);
-
- if (k == 0) {
- /* No refinement was found needed - return current curve. */
- IritFree((VoidPtr) RefKV);
- break;
- }
- else {
- CagdCrvStruct
- *CTmp = CagdCrvRefineAtParams(Crv, FALSE, RefKV, k);
-
- IritFree((VoidPtr) RefKV);
- CagdCrvFree(Crv);
- Crv = CTmp;
- }
-
- }
- }
-
- CagdCrvFree(Crv);
-
- /* Error is probably within bounds - returns this unit length approx. */
- if (Mult) {
- CagdCrvStruct *CrvX, *CrvY, *CrvZ, *CrvW, *CTmp;
- int MaxCoord = CAGD_NUM_OF_PT_COORD(OrigCrv -> PType);
-
- CagdCrvSplitScalar(ScalarCrv, &CrvW, &CrvX, &CrvY, &CrvZ);
- CagdCrvFree(ScalarCrv);
- ScalarCrv = CagdCrvMergeScalar(CrvW,
- CrvX,
- MaxCoord > 1 ? CrvX : NULL,
- MaxCoord > 2 ? CrvX : NULL);
- CagdCrvFree(CrvX);
- if (CrvW)
- CagdCrvFree(CrvW);
-
- CTmp = CagdCrvMult(ScalarCrv, OrigCrv);
- CagdCrvFree(ScalarCrv);
-
- return CTmp;
- }
- else {
- return ScalarCrv;
- }
- }
-
- /******************************************************************************
- * Computes the curve which is a square root approximation to a given scalar *
- * curve, to within epsilon. *
- ******************************************************************************/
- CagdCrvStruct *CagdCrvSqrtScalar(CagdCrvStruct *OrigCrv, CagdRType Epsilon)
- {
- int i, j;
- CagdCrvStruct
- *ScalarCrv = NULL,
- *Crv = CAGD_IS_BEZIER_CRV(OrigCrv) ? CnvrtBezier2BsplineCrv(OrigCrv)
- : CagdCrvCopy(OrigCrv);
- CagdBType
- IsRational = CAGD_IS_RATIONAL_CRV(Crv);
-
- for (i = 0; i < MAX_ALEN_IMPROVE_ITERS; i++) {
- CagdCrvStruct *ScalarCrvSqr, *ErrorCrv;
- CagdRType Min, Max, *SCPoints,
- *Points = Crv -> Points[1],
- *WPoints = IsRational ? Crv -> Points[0] : NULL;
-
- if (ScalarCrv)
- CagdCrvFree(ScalarCrv);
- ScalarCrv = CagdCrvCopy(Crv);
- SCPoints = ScalarCrv -> Points[1];
-
- /* Compute an approximation to the inverse function. */
- for (j = 0; j < ScalarCrv -> Length; j++) {
- CagdRType
- V = IsRational ? *Points++ / *WPoints++ : *Points++;
-
- *SCPoints++ = V >= 0.0 ? sqrt(V) : 0.0;
- }
- ScalarCrvSqr = CagdCrvMult(ScalarCrv, ScalarCrv);
-
- /* Compare the two to test the error. */
- ErrorCrv = CagdCrvSub(ScalarCrvSqr, Crv);
- CagdCrvFree(ScalarCrvSqr);
-
- CagdCrvMinMax(ErrorCrv, 1, &Min, &Max);
-
- if (Min > -Epsilon && Max < Epsilon) {
- CagdCrvFree(ErrorCrv);
- break;
- }
- else {
- /* Refine in regions where the error is too large. */
- int k,
- Length = ErrorCrv -> Length,
- Order = ErrorCrv -> Order,
- KVLen = Length + Order;
- CagdRType
- *KV = ErrorCrv -> KnotVector,
- *RefKV = IritMalloc(sizeof(CagdRType) * 2 * Length),
- *Nodes = BspKnotNodes(KV, KVLen, Order),
- **ErrPoints = ErrorCrv -> Points;
-
- for (j = k = 0; j < Length; j++) {
- CagdRType
- V = 1.0 - (IsRational ? ErrPoints[1][j] / ErrPoints[0][j]
- : ErrPoints[1][j]);
-
- if (ABS(V) > Epsilon) {
- int Index = BspKnotLastIndexLE(KV, KVLen, Nodes[j]);
-
- if (APX_EQ(KV[Index], Nodes[j])) {
- if (j > 0)
- RefKV[k++] = (Nodes[j] + Nodes[j - 1]) / 2.0;
- if (j < Length - 1)
- RefKV[k++] = (Nodes[j] + Nodes[j + 1]) / 2.0;
- }
- else
- RefKV[k++] = Nodes[j];
- }
- }
- CagdCrvFree(ErrorCrv);
-
- IritFree((VoidPtr) Nodes);
-
- if (k == 0) {
- /* No refinement was found needed - return current curve. */
- IritFree((VoidPtr) RefKV);
- break;
- }
- else {
- CagdCrvStruct
- *CTmp = CagdCrvRefineAtParams(Crv, FALSE, RefKV, k);
-
- IritFree((VoidPtr) RefKV);
- CagdCrvFree(Crv);
- Crv = CTmp;
- }
-
- }
- }
-
- CagdCrvFree(Crv);
-
- return ScalarCrv;
- }
-
- /******************************************************************************
- * Normalizes the given curve to be a unit length curve, by computing a scalar *
- * curve to multiply with this curve. Returns the composed curve if Compose, *
- * or just the scalar curve, otherwise. *
- * Note that the returned curves is approximately constant speed curve and *
- * not arc-length, since a Bezier curve always has a domain 0 to 1 nomatter *
- * the size of the curve itself. *
- ******************************************************************************/
- CagdCrvStruct *CagdCrvArcLenCrv(CagdCrvStruct *Crv, CagdRType Epsilon)
- {
- CagdCrvStruct
- *DerivCrv = CagdCrvDerive(Crv),
- *DerivMagSqrCrv = CagdCrvDotProd(DerivCrv, DerivCrv),
- *DerivMagCrv = CagdCrvSqrtScalar(DerivMagSqrCrv, Epsilon),
- *ArcLenCrv = CagdCrvIntegrate(DerivMagCrv);
-
- CagdCrvFree(DerivCrv);
- CagdCrvFree(DerivMagSqrCrv);
- CagdCrvFree(DerivMagCrv);
-
- return ArcLenCrv;
- }
-
- /******************************************************************************
- * Computes a tight approximation to the arc length of a curve. *
- ******************************************************************************/
- CagdRType CagdCrvArcLen(CagdCrvStruct *Crv, CagdRType Epsilon)
- {
- CagdRType TMin, TMax, *Pt;
- CagdCrvStruct
- *ArcLenCrv = CagdCrvArcLenCrv(Crv, Epsilon);
-
- CagdCrvDomain(ArcLenCrv, &TMin, &TMax);
- Pt = CagdCrvEval(ArcLenCrv, TMax);
- CagdCrvFree(ArcLenCrv);
-
- return CAGD_IS_RATIONAL_CRV(ArcLenCrv) ? Pt[1] / Pt[0] : Pt[1];
- }
-
- /******************************************************************************
- * Computes parameter values to move steps of Length at a time. Returned is a *
- * list of parameter values to step at. *
- ******************************************************************************/
- CagdPtStruct *CagdCrvArcLenSteps(CagdCrvStruct *Crv, CagdRType Length,
- CagdRType Epsilon)
- {
- CagdRType TMin, TMax, *Pt, CrvLength, Len;
- CagdPtStruct *Param,
- *ParamList = NULL;
- CagdCrvStruct
- *ArcLenCrv = CagdCrvArcLenCrv(Crv, Epsilon);
-
- CagdCrvDomain(ArcLenCrv, &TMin, &TMax);
- Pt = CagdCrvEval(ArcLenCrv, TMax);
-
- CrvLength = CAGD_IS_RATIONAL_CRV(ArcLenCrv) ? Pt[1] / Pt[0] : Pt[1];
-
- for (Len = CrvLength - Length; Len > 0.0; Len -= Length) {
- Param = CagdCrvConstSet(ArcLenCrv, 1, ARCLEN_CONST_SET_EPSILON, Len);
- if (Param == NULL || Param -> Pnext != NULL)
- FATAL_ERROR(CAGD_ERR_REPARAM_NOT_MONOTONE);
-
- Param -> Pnext = ParamList;
- ParamList = Param;
- }
-
- CagdCrvFree(ArcLenCrv);
-
- return ParamList;
- }
-